package com.think.cloud.thinkshop.mall.service.coupon.impl;

import cn.hutool.core.util.ObjectUtil;
import com.alibaba.csp.sentinel.util.StringUtil;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.think.cloud.thinkshop.common.enums.RabbitMessageTypeEnum;
import com.think.cloud.thinkshop.mall.controller.admin.coupon.vo.*;
import com.think.cloud.thinkshop.mall.convert.coupon.ProductCouponConvert;
import com.think.cloud.thinkshop.mall.domain.coupon.ProductCoupon;
import com.think.cloud.thinkshop.mall.enums.coupon.CouponExpirationEnum;
import com.think.cloud.thinkshop.mall.enums.coupon.CouponStatusEnum;
import com.think.cloud.thinkshop.mall.mapper.coupon.ProductCouponMapper;
import com.think.cloud.thinkshop.mall.mapper.coupon.ProductCouponRelationMapper;
import com.think.cloud.thinkshop.mall.mapper.order.OrderMapper;
import com.think.cloud.thinkshop.mall.rabbit.producer.MessageProducer;
import com.think.cloud.thinkshop.mall.service.coupon.IProductCouponService;
import com.think.common.core.exception.enums.ErrorCode;
import com.think.common.core.exception.util.ServiceExceptionUtil;
import com.think.common.core.utils.DateUtils;
import com.think.common.core.utils.PageUtils;
import com.think.common.core.web.page.TableDataInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import static com.think.cloud.thinkshop.mall.enums.coupon.CouponStatusEnum.*;

/**
 * 商品优惠券Service业务层处理
 *
 * @author zkthink
 * @date 2024-05-21
 */
@Service
public class ProductCouponServiceImpl implements IProductCouponService {

    @Autowired
    private ProductCouponMapper productCouponMapper;

    @Autowired
    private MessageProducer messageProducer;

    @Autowired
    private OrderMapper orderMapper;

    @Autowired
    private ProductCouponRelationMapper productCouponRelationMapper;

    /**
     * 查询商品优惠券
     *
     * @param id 商品优惠券主键
     * @return 商品优惠券
     */
    @Override
    public ProductCouponDetailRespVO selectProductCouponById(Long id) {
        ProductCoupon coupon = getCouponById(id);
        if (StringUtil.isNotBlank(coupon.getScopeValues())) {
            coupon.setScopeValues(convertArr(coupon.getScopeValues()));
        }
        return ProductCouponConvert.INSTANCE.convert(coupon);
    }

    /**
     * 查询商品优惠券列表
     *
     * @param vo
     * @return 商品优惠券
     */
    @Override
    public TableDataInfo selectProductCouponList(ProductCouponPageReqVO vo) {
        List<ProductCoupon> productCoupons = productCouponMapper.selectList(vo);
        List<ProductCouponDetailRespVO> list =
                ProductCouponConvert.INSTANCE.convertList(productCoupons);
        list.forEach(res -> {
            res.setStatusName(CouponStatusEnum.toEnum(res.getStatus()).getDesc());
        });
        return new TableDataInfo(list, PageUtils.getTotal(productCoupons));
    }

    /**
     * 新增商品优惠券
     *
     * @param vo
     * @return 结果
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long insertProductCoupon(ProductCouponCreateReqVO vo) {
        ProductCoupon coupon = ProductCouponConvert.INSTANCE.convert(vo);
        coupon.setCouponCode(UUID.randomUUID().toString());
        this.buildCoupon(coupon,vo.getIds());
        productCouponMapper.insert(coupon);
        // 发送延时消息
        sendCouponDelayedMessage(coupon);
        return coupon.getId();
    }

    /**
     * 修改商品优惠券
     *
     * @param vo
     * @return 结果
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long updateProductCoupon(ProductCouponUpdateReqVO vo) {
        ProductCoupon coupon = ProductCouponConvert.INSTANCE.convert(vo);
        this.buildCoupon(coupon,vo.getIds());
        updateCouponById(coupon);
        // 发送延时消息
        sendCouponDelayedMessage(coupon);
        return coupon.getId();
    }

    private void  buildCoupon(ProductCoupon coupon,String[] ids){
        if (ids != null && ids.length > 0) {
            coupon.setScopeValues(convertString(ids));
        }
        if (CouponExpirationEnum.BY_DAY.getValue().equals(coupon.getExpirationType())) {
            coupon.setStatus(IN_PROGRESS.getValue());
        } else {
            coupon.setStatus(determineStatus(coupon.getTakingEffectTime(), coupon.getExpirationTime()));
        }
    }

    private void sendCouponDelayedMessage(ProductCoupon coupon) {
        if (CouponExpirationEnum.BY_DAY.getValue().equals(coupon.getExpirationType())) return;
        if (NOT_STARTED.getValue().equals(coupon.getStatus())) {
            send(coupon.getId(), coupon.getTakingEffectTime(), RabbitMessageTypeEnum.DELAY_COUPON_START);
        } else if (IN_PROGRESS.getValue().equals(coupon.getStatus())) {
            send(coupon.getId(), coupon.getExpirationTime(), RabbitMessageTypeEnum.DELAY_COUPON_END);
        }
    }

    /**
     * 判断优惠券状态
     *
     * @param startTime
     * @param endTime
     * @return
     */
    private Integer determineStatus(Timestamp startTime, Timestamp endTime) {
        if (DateUtils.isDifferenceOverAYear(startTime, endTime))
            throw ServiceExceptionUtil.exception(ErrorCode.COUPON_DATE_INTERVAL_ERROR);
        // 其他情况为结束状态
        Integer status = ENDED.getValue();
        if (ObjectUtil.isNotNull(startTime) && ObjectUtil.isNotNull(endTime)) {
            if (DateUtils.isBelong(startTime, endTime)) {
                // 如果当前时间在这个范围内,状态为进行中
                status = IN_PROGRESS.getValue();
            } else if (DateUtils.isBefore(startTime)) {
                // 如果活动开始时间大于当前时间,状态为未开始
                status = NOT_STARTED.getValue();
            }
        }
        return status;
    }

    /**
     * 批量删除商品优惠券信息
     *
     * @param ids 主键集合
     * @return 结果
     */
    @Override
    public int batchDelete(List<Long> ids) {
        return productCouponMapper.deleteBatchIds(ids);
    }

    @Override
    public void end(Long id) {
        updateCouponById(ProductCoupon.builder().id(id).status(ENDED.getValue()).build());
    }

    @Override
    public void autoSwitch(Long id) {
        ProductCoupon coupon = getCouponById(id);
        if (NOT_STARTED.getValue().equals(coupon.getStatus())) {
            if (sectionDelay(id, coupon.getTakingEffectTime(), RabbitMessageTypeEnum.DELAY_COUPON_START)) {
                return;
            }
            send(id, coupon.getExpirationTime(), RabbitMessageTypeEnum.DELAY_COUPON_END);
            coupon.setStatus(IN_PROGRESS.getValue());
            updateCouponById(coupon);
        } else if (IN_PROGRESS.getValue().equals(coupon.getStatus())) {
            if (sectionDelay(id, coupon.getExpirationTime(), RabbitMessageTypeEnum.DELAY_COUPON_END)) {
                return;
            }
            end(id);
        }
    }

    /**
     * 分段延时执行
     *
     * @param id
     * @param time
     * @param typeEnum
     * @return
     */
    private boolean sectionDelay(Long id, Timestamp time, RabbitMessageTypeEnum typeEnum) {
        // 计算延时时间
        long delay = (time.getTime() - System.currentTimeMillis());
        if (delay > 0) send(id, time, typeEnum);
        return delay > 0;
    }

    @Override
    public ProductCouponDataRespVO data(Long id) {
        ProductCoupon coupon = getCouponById(id);
        ProductCouponDataRespVO result = new ProductCouponDataRespVO();
        result.setCouponName(coupon.getCouponName());
        Long receivePerson = productCouponRelationMapper.getReceivePerson(id);
        result.setReceivePerson(ObjectUtil.isNull(receivePerson) ? 0 : receivePerson);
        result.setReceiveNumber(coupon.getReceivedNumber());
        result.setResidueNumber(coupon.getNumber() - coupon.getReceivedNumber());
        BigDecimal totalAmount = BigDecimal.ZERO;   //交易总额
        BigDecimal totalCouponAmount = BigDecimal.ZERO; // 优惠总额
        long totalNumber = 0l;  // 商品总数
        BigDecimal averageCouponAmount = BigDecimal.ZERO;   // 平均优惠金额
        // 获取优惠订单明细
        List<ProductCouponOrderDetailRespVO> orderDetails = orderMapper.getCouponDataDetails(id);
        if (CollectionUtils.isNotEmpty(orderDetails)) {
            // 计算交易总额
            totalAmount = orderDetails.stream()
                    .map(ProductCouponOrderDetailRespVO::getPrice).reduce(BigDecimal::add).get();
            // 计算优惠总额
            totalCouponAmount = orderDetails.stream()
                    .map(ProductCouponOrderDetailRespVO::getCouponPrice).reduce(BigDecimal::add).get();
            // 计算商品总数
            totalNumber = orderDetails.size();
            // 计算平均优惠金额
            averageCouponAmount = totalCouponAmount.divide(new BigDecimal(totalNumber));
            Map<String, List<ProductCouponOrderDetailRespVO>> map =
                    orderDetails.stream().collect(Collectors.groupingBy(ProductCouponOrderDetailRespVO::getProductName));
            List<ProductCouponDataDetailRespVO> couponDataDetails = new ArrayList<>();
            for (String productName : map.keySet()) {
                ProductCouponDataDetailRespVO couponDataDetail = new ProductCouponDataDetailRespVO();
                couponDataDetail.setProductName(productName);
                List<ProductCouponOrderDetailRespVO> productOrderDetails = map.get(productName);
                // 计算商品下单人数
                couponDataDetail.setPerson(productOrderDetails.stream()
                        .map(ProductCouponOrderDetailRespVO::getUserId).distinct().count());
                // 计算商品数
                couponDataDetail.setNumber((long) productOrderDetails.size());
                couponDataDetails.add(couponDataDetail);
            }
            result.setDetails(couponDataDetails);
        }
        result.setTotalAmount(totalAmount);
        result.setTotalCouponAmount(totalCouponAmount);
        result.setTotalNumber(totalNumber);
        result.setAverageCouponAmount(averageCouponAmount);
        return result;
    }

    @Override
    public List<ProductCoupon> selectProductCouponByIds(List<Long> couponIds) {
        return productCouponMapper.selectBatchIds(couponIds);
    }

    private ProductCoupon getCouponById(Long id) {
        ProductCoupon coupon = productCouponMapper.selectById(id);
        if (ObjectUtil.isNull(coupon)) throw ServiceExceptionUtil.exception(ErrorCode.PRODUCT_COUPON_NOT_EXISTS);
        return coupon;
    }

    private void updateCouponById(ProductCoupon  coupon) {
          productCouponMapper.updateById(coupon);
    }


    private void send(Long id, Timestamp time, RabbitMessageTypeEnum typeEnum) {
        // 计算延时时间
        long delay = (time.getTime() - System.currentTimeMillis());
        if (delay > Integer.MAX_VALUE) delay = Integer.MAX_VALUE;    // 分段延时
        messageProducer.sendDelayedMessage(id.toString(), typeEnum, delay, TimeUnit.MILLISECONDS);
    }

    private String convertString(String[] arr) {
        return String.join(",", arr);
    }

    private String convertArr(String value) {
        return "[" + value + "]";
    }
}
